package com.wei.czz.common.utils;

import com.wei.czz.common.exception.CzzException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Objects;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wyw
 * date: 2024-11-28 11:43:20
 * className: ShaUtils
 * version: 1.0
 * description:
 */
public class ShaUtils {

    private static final Logger log = LoggerFactory.getLogger(ShaUtils.class);

    /**
     * sha256加密算法
     */
    private static final String SHA256 = "SHA-256";

    /**
     * sha256加密
     * @param plainText 明文
     * @return 十六进制密文
     */
    public static String encodeSha256(String plainText) {
        return encodeSha256(plainText, null);
    }

    /**
     * sha256加密
     * @param plainText 明文
     * @param salt      盐
     * @return 十六进制密文
     */
    public static String encodeSha256(String plainText, String salt) {
        if (StringUtils.isBlank(plainText)) {
            log.info("sha256加密传递的明文字符串为空");
            throw new CzzException();
        }
        if (StringUtils.isNotBlank(salt)) {
            // 加密盐拼接到明文后面
            plainText = plainText.concat(salt);
        }
        try {
            // 获取算法加密对象
            MessageDigest sha256 = MessageDigest.getInstance(SHA256);
            // 加密
            byte[] hash = sha256.digest(plainText.getBytes(StandardCharsets.UTF_8));
            // 密文转换成十六进制字符串
            return toHex(hash);
        } catch (Exception e) {
            log.error("sha256加密异常。message={}", e.getMessage(), e);
            throw new CzzException();
        }
    }

    public static String encodeSha256(MultipartFile file) {
        try {
            // 获取算法加密对象
            MessageDigest sha256 = MessageDigest.getInstance(SHA256);
            // 加密
            byte[] hash = sha256.digest(file.getBytes());
            // 密文转换成十六进制字符串
            return toHex(hash);
        } catch (Exception e) {
            log.error("文件sha256加密异常。message={}", e.getMessage(), e);
            throw new CzzException();
        }
    }

    /**
     * 文件sha256加密
     * @param file 文件对象
     * @return 密文
     */
    public static String fileSha256(File file) {
        if (Objects.isNull(file)) {
            log.error("文件参数为空");
            throw new CzzException();
        }
        if (!file.exists()) {
            log.error("文件不存在");
            throw new CzzException();
        }
        // 获取加密对象
        MessageDigest messageDigest;
        try {
            messageDigest = MessageDigest.getInstance(SHA256);
        } catch (NoSuchAlgorithmException e) {
            log.error("获取Sha256加密对象失败。 message={}", e.getMessage(), e);
            throw new CzzException();
        }

        try (FileInputStream fis = new FileInputStream(file);
             FileChannel channel = fis.getChannel()) {

            // 动态计算缓冲区大小(8KB-1MB之间)
            ByteBuffer buffer = FileUtils.dinamicByteBuffer(file.length());
            while (channel.read(buffer) != -1) {
                // 切换为读模式
                buffer.flip();
                // 将缓冲区内容写入到消息摘要中
                messageDigest.update(buffer);
                // 清空缓冲区，准备下一次写入
                buffer.clear();
            }
            // 完成文件读取后，将缓冲区内容写入到消息摘要中
            byte[] result = messageDigest.digest();
            // 结果封装
            return ByteUtils.bytesToHex(result);

        } catch (FileNotFoundException e) {
            log.error("sha256加密失败，文件不存在: {}", e.getMessage(), e);
            throw new CzzException();
        } catch (IOException e) {
            log.error("sha256加密失败，文件读取失败: {}", e.getMessage(), e);
            throw new CzzException();
        }
    }

    /**
     * 文件sha256加密
     * @param inputStream 文件输入流
     * @param fileSize    文件大小
     * @return 密文
     */
    public static String fileSha256(InputStream inputStream, long fileSize) {
        // 获取加密对象
        MessageDigest messageDigest;
        try {
            messageDigest = MessageDigest.getInstance(SHA256);
        } catch (NoSuchAlgorithmException e) {
            log.error("获取Sha256加密对象失败。 message={}", e.getMessage(), e);
            throw new CzzException();
        }

        try(ReadableByteChannel channel = Channels.newChannel(inputStream)) {

            // 动态计算缓冲区大小(8KB-1MB之间)
            ByteBuffer buffer = FileUtils.dinamicByteBuffer(fileSize);
            while (channel.read(buffer) != -1) {
                // 切换为读模式
                buffer.flip();
                // 将缓冲区内容写入到消息摘要中
                messageDigest.update(buffer);
                // 清空缓冲区，准备下一次写入
                buffer.clear();
            }
            // 完成文件读取后，将缓冲区内容写入到消息摘要中
            byte[] result = messageDigest.digest();
            // 结果封装
            return ByteUtils.bytesToHex(result);

        } catch (IOException e) {
            log.error("文件输入流sha256加密失败。 message={}", e.getMessage(), e);
            throw new CzzException();
        }
    }

    /**
     * 将字节数组转换为十六进制字符串
     *
     * @param hash 字节数组
     * @return 十六进制字符串
     */
    private static String toHex(byte[] hash) {
        // 结果缓存数组
        char[] result = new char[hash.length * 2];

        char[] hexArray = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
        int index = 0;
        for (byte b : hash) {
            result[index++] = hexArray[b >>> 4 & 15];
            result[index++] = hexArray[b & 15];
        }
        return new String(result);
    }

}
